package no.uib.jexpress_modularized.core.visualization.charts;

import java.awt.*;
import java.io.Serializable;

import java.util.*;

import java.lang.*;


public class ChartLabel extends Object implements Serializable{

    /*
     *
     *************
     *
     **
     *
     ** Constants
     *
     **
     *
     ***********
     */
    /**
     *
     * Center the Text over the point
     *
     */
    public final static int CENTER = 0;
    /**
     *
     * Position the Text to the Left of the point
     *
     */
    public final static int LEFT = 1;
    /**
     *
     * Position the Text to the Right of the point
     *
     */
    public final static int RIGHT = 2;
    /**
     *
     * Format to use when parsing a double
     *
     */
    public final static int SCIENTIFIC = 1;
    /**
     *
     * Format to use when parsing a double
     *
     */
    public final static int ALGEBRAIC = 2;
    /*
     *
     ** Minimum Point size allowed for script characters
     *
     */
    final static int MINIMUM_SIZE = 6;
    /*
     *
     **********************
     *
     **
     *
     ** Protected Variables
     *
     **
     *
     ********************
     */
    /**
     *
     * Decrease in size of successive script levels
     *
     */
    protected double script_fraction = 0.8;
    /**
     *
     * Superscript offset
     *
     */
    protected double sup_offset = 0.6;
    /**
     *
     * Subscript offset
     *
     */
    protected double sub_offset = 0.7;
    /**
     *
     * Font to use for text
     *
     */
    protected Font font = null;
    /**
     *
     * Text color
     *
     */
    protected Color color = null;
    /**
     *
     * Background Color
     *
     */
    protected Color background = null;
    /**
     *
     * The text to display
     *
     */
    protected String text = null;
    /**
     *
     * The logical name of the font to use
     *
     */
    protected String fontname = "TimesRoman";
    /**
     *
     * The font size
     *
     */
    protected int fontsize = 0;
    /**
     *
     * The font style
     *
     */
    protected int fontstyle = Font.PLAIN;
    /**
     *
     * Text justification. Either CENTER, LEFT or RIGHT
     *
     */
    protected int justification = LEFT;
    /**
     *
     * The width of the text using the current Font
     *
     */
    protected int width = 0;
    /**
     *
     * The ascent using the current font
     *
     */
    protected int ascent = 0;
    /**
     *
     * The maximum ascent using the current font
     *
     */
    protected int maxAscent = 0;
    /**
     *
     * The descent using the current font
     *
     */
    protected int descent = 0;
    /**
     *
     * The maximum descent using the current font
     *
     */
    protected int maxDescent = 0;
    /**
     *
     * The height using the current font ie ascent+descent+leading
     *
     */
    protected int height = 0;
    /**
     *
     * The leading using the current font
     *
     */
    protected int leading = 0;
    /**
     *
     * Has the string been parsed! This only needs to be done once
     *
     * unless the font is altered.
     *
     */
    protected boolean parse = true;
    /**
     *
     * Local graphics context.
     *
     */
    protected Graphics lg = null;
    /**
     *
     * The parsed string. Each element in the vector represents
     *
     * a change of context in the string ie font change and offset.
     *
     */
    protected Vector list = new Vector(8, 4);

    /*
     *
     **********************
     *
     **
     *
     ** Constructors
     *
     **
     *
     ********************
     */
    /**
     *
     * Instantiate the class
     *
     */
    public ChartLabel() {
    }

    /**
     *
     * Instantiate the class.
     *
     * @param s String to parse.
     *
     */
    public ChartLabel(String s) {

        this.text = s;

    }

    /**
     *
     * Instantiate the class
     *
     * @param s String to parse.
     *
     * @param f Font to use.
     *
     */
    public ChartLabel(String s, Font f) {

        this(s);

        font = f;

        if (font == null) {
            return;
        }

        fontname = f.getName();

        fontstyle = f.getStyle();

        fontsize = f.getSize();

    }

    /**
     *
     * Instantiate the class
     *
     * @param s String to parse.
     *
     * @param f Font to use.
     *
     * @param c Color to use
     *
     * @param j Justification
     *
     */
    public ChartLabel(String s, Font f, Color c, int j) {

        this(s, f);

        color = c;

        justification = j;

    }

    /**
     *
     * Instantiate the class
     *
     * @param s String to parse.
     *
     * @param c Color to use
     *
     */
    public ChartLabel(String s, Color c) {

        this(s);

        color = c;

    }

    /**
     *
     * Instantiate the class
     *
     * @param f Font to use.
     *
     * @param c Color to use
     *
     * @param j Justification
     *
     */
    public ChartLabel(Font f, Color c, int j) {

        font = f;

        color = c;

        justification = j;



        if (font == null) {
            return;
        }

        fontname = f.getName();

        fontstyle = f.getStyle();

        fontsize = f.getSize();

    }

    /*
     *
     *****************
     *
     **
     *
     ** Public Methods
     *
     **
     *
     ****************
     */
    /**
     *
     * Create a New ChartLabel object copying the state of the existing
     *
     * object into the new one. The state of the class is the color,
     *
     * font, and justification ie everything but the string.
     *
     */
    public ChartLabel copyState() {

        return new ChartLabel(font, color, justification);

    }

    /**
     *
     * Copy the state of the parsed ChartLabel into the existing
     *
     * object.
     *
     * @param t The ChartLabel to get the state information from.
     *
     */
    public void copyState(ChartLabel t) {

        if (t == null) {
            return;
        }



        font = t.getFont();

        color = t.getColor();

        justification = t.getJustification();



        if (font == null) {
            return;
        }

        fontname = font.getName();

        fontstyle = font.getStyle();

        fontsize = font.getSize();



        parse = true;

    }

    /**
     *
     * Set the Font to use with the class
     *
     * @param f Font
     *
     */
    public void setFont(Font f) {

        font = f;

        fontname = f.getName();

        fontstyle = f.getStyle();

        fontsize = f.getSize();

        parse = true;



    }

    /**
     *
     * Set the String to use with the class
     *
     * @param s String
     *
     */
    public void setText(String s) {

        text = s;

        parse = true;

    }

    /**
     *
     * Set the Color to use with the class
     *
     * @param c Color
     *
     */
    public void setColor(Color c) {

        color = c;

    }

    /**
     *
     * Set the Background Color to use with the class
     *
     * @param c Color
     *
     */
    public void setBackground(Color c) {

        background = c;

    }

    /**
     *
     * Set the Justification to use with the class
     *
     * @param t Justification
     *
     */
    public void setJustification(int i) {

        switch (i) {

            case CENTER:

                justification = CENTER;

                break;

            case LEFT:
            default:

                justification = LEFT;

                break;

            case RIGHT:

                justification = RIGHT;

                break;

        }

    }

    /**
     *
     * @return the font the class is using
     *
     */
    public Font getFont() {

        return font;

    }

    /**
     *
     * @return the String the class is using.
     *
     */
    public String getText() {

        return text;

    }

    /**
     *
     * @return the Color the class is using.
     *
     */
    public Color getColor() {

        return color;

    }

    /**
     *
     * @return the Background Color the class is using.
     *
     */
    public Color getBackground() {

        return background;

    }

    /**
     *
     * @return the Justification the class is using.
     *
     */
    public int getJustification() {

        return justification;

    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the Fontmetrics the class is using.
     *
     */
    public FontMetrics getFM(Graphics g) {

        if (g == null) {
            return null;
        }



        if (font == null) {
            return g.getFontMetrics();
        } else {
            return g.getFontMetrics(font);
        }

    }

    /**
     *
     * @param g Graphics context.
     *
     * @param ch The character.
     *
     * @return the width of the character.
     *
     */
    public int charWidth(Graphics g, char ch) {

        FontMetrics fm;

        if (g == null) {
            return 0;
        }



        if (font == null) {
            fm = g.getFontMetrics();
        } else {
            fm = g.getFontMetrics(font);
        }



        return fm.charWidth(ch);

    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the width of the parsed text.
     *
     */
    public int getWidth(Graphics g) {



        parseText(g);



        return width;



    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the height of the parsed text.
     *
     */
    public int getHeight(Graphics g) {



        parseText(g);



        return height;



    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the ascent of the parsed text.
     *
     */
    public int getAscent(Graphics g) {

        if (g == null) {
            return 0;
        }



        parseText(g);



        return ascent;

    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the maximum ascent of the parsed text.
     *
     */
    public int getMaxAscent(Graphics g) {

        if (g == null) {
            return 0;
        }



        parseText(g);



        return maxAscent;

    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the descent of the parsed text.
     *
     */
    public int getDescent(Graphics g) {

        if (g == null) {
            return 0;
        }



        parseText(g);



        return descent;



    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the maximum descent of the parsed text.
     *
     */
    public int getMaxDescent(Graphics g) {

        if (g == null) {
            return 0;
        }



        parseText(g);



        return maxDescent;

    }

    /**
     *
     * @param g Graphics context.
     *
     * @return the leading of the parsed text.
     *
     */
    public int getLeading(Graphics g) {

        if (g == null) {
            return 0;
        }



        parseText(g);



        return leading;



    }

    /**
     *
     * parse the text. When the text is parsed the width, height, leading
     *
     * are all calculated. The text will only be truly parsed if
     *
     * the graphics context has changed or the text has changed or
     *
     * the font has changed. Otherwise nothing is done when this
     *
     * method is called.      *
     * @param g Graphics context.
     *
     */
    public void parseText(Graphics g) {

        FontMetrics fm;

        TextState current = new TextState();

        char ch;

        Stack state = new Stack();

        int w = 0;



        if (lg != g) {
            parse = true;
        }

        lg = g;



        if (!parse) {
            return;
        }



        parse = false;

        width = 0;

        leading = 0;

        ascent = 0;

        descent = 0;

        height = 0;

        maxAscent = 0;

        maxDescent = 0;



        if (text == null || g == null) {
            return;
        }



        list.removeAllElements();





        if (font == null) {
            current.f = g.getFont();
        } else {
            current.f = font;
        }



        state.push(current);

        list.addElement(current);



        fm = g.getFontMetrics(current.f);



        for (int i = 0; i < text.length(); i++) {

            ch = text.charAt(i);



            switch (ch) {



                case '$':

                    i++;

                    if (i < text.length()) {
                        current.s.append(text.charAt(i));
                    }

                    break;

                /*
                 *
                 ** Push the current state onto the state stack
                 *
                 ** and start a new storage string
                 *
                 */

                case '{':

                    w = current.getWidth(g);

                    if (!current.isEmpty()) {

                        current = current.copyState();

                        list.addElement(current);

                    }



                    state.push(current);

                    current.x += w;

                    break;

                /*
                 *
                 ** Pop the state off the state stack and set the current
                 *
                 ** state to the top of the state stack
                 *
                 */

                case '}':

                    w = current.x + current.getWidth(g);

                    state.pop();

                    current = ((TextState) state.peek()).copyState();

                    list.addElement(current);

                    current.x = w;

                    break;

                case '^':

                    w = current.getWidth(g);

                    if (!current.isEmpty()) {

                        current = current.copyState();

                        list.addElement(current);

                    }

                    current.f = getScriptFont(current.f);

                    current.x += w;

                    current.y -= (int) ((double) (current.getAscent(g)) * sup_offset + 0.5);

                    break;

                case '_':

                    w = current.getWidth(g);

                    if (!current.isEmpty()) {

                        current = current.copyState();

                        list.addElement(current);

                    }

                    current.f = getScriptFont(current.f);

                    current.x += w;

                    current.y += (int) ((double) (current.getDescent(g)) * sub_offset + 0.5);

                    break;



                case '%':

                    w = current.getWidth(g);

                    if (!current.isEmpty()) {

                        current = current.copyState();

                        list.addElement(current);

                    }

                    current.f = getScriptFont(current.f);

                    current.x += w;

                    current.y += (int) ((double) (current.getDescent(g)) * sub_offset + 0.2);

                    break;







                default:

                    current.s.append(ch);

                    break;

            }

        }







        for (int i = 0; i < list.size(); i++) {

            current = ((TextState) (list.elementAt(i)));



            if (!current.isEmpty()) {

                width += current.getWidth(g);

                ascent = Math.max(ascent, Math.abs(current.y)
                        + current.getAscent(g));

                descent = Math.max(descent, Math.abs(current.y)
                        + current.getDescent(g));

                leading = Math.max(leading, current.getLeading(g));



                maxDescent = Math.max(maxDescent, Math.abs(current.y)
                        + current.getMaxDescent(g));

                maxAscent = Math.max(maxAscent, Math.abs(current.y)
                        + current.getMaxAscent(g));

            }

        }



        height = ascent + descent + leading;



        return;



    }

    /**
     *
     * @return true if the text has never been set or is null
     *
     */
    public boolean isNull() {

        return (text == null);

    }

    /**
     *
     * Parse the text then draw it.
     *
     * @param g Graphics context
     *
     * @param x pixel position of the text
     *
     * @param y pixel position of the text
     *
     * @param j justification of the text
     *
     */
    public void draw(Graphics g, int x, int y, int j) {

        justification = j;



        if (g == null) {
            return;
        }



        draw(g, x, y);

    }

    /**
     *
     * Parse the text then draw it without any rotation.
     *
     * @param g Graphics context
     *
     * @param x pixel position of the text
     *
     * @param y pixel position of the text
     *
     */
    public void draw(Graphics g, int x, int y) {

        TextState ts;

        int xoffset = x;

        int yoffset = y;





        if (g == null || text == null) {
            return;
        }





        Graphics lg = g.create();



        parseText(g);





        if (justification == CENTER) {

            xoffset = x - width / 2;

        } else if (justification == RIGHT) {

            xoffset = x - width;

        }



        if (background != null) {

            lg.setColor(background);

            lg.fillRect(xoffset, yoffset - ascent, width, height);

            lg.setColor(g.getColor());

        }



        if (font != null) {
            lg.setFont(font);
        }

        if (color != null) {
            lg.setColor(color);
        }



        for (int i = 0; i < list.size(); i++) {

            ts = ((TextState) (list.elementAt(i)));

            if (ts.f != null) {
                lg.setFont(ts.f);
            }

            if (ts.s != null) {
                if (ts.toString().length() > 0) {
                    lg.drawString(ts.toString(), ts.x + xoffset, ts.y + yoffset);
                }
            }

        }



        lg.dispose();



        lg = null;



    }

    /**
     *
     * @return Logical font name of the set font
     *
     */
    public String getFontName() {
        return fontname;
    }

    /**
     *
     * @return Style of the set font
     *
     */
    public int getFontStyle() {
        return fontstyle;
    }

    /**
     *
     * @return Size of the set font
     *
     */
    public int getFontSize() {
        return fontsize;
    }

    /**
     *
     * Set the Logical font name of the current font
     *
     * @param s Logical font name.
     *
     */
    public void setFontName(String s) {
        fontname = s;
        rebuildFont();
    }

    /**
     *
     * Set the Font style of the current font
     *
     * @param i Font style.
     *
     */
    public void setFontStyle(int i) {
        fontstyle = i;
        rebuildFont();
    }

    /**
     *
     * Set the Font size of the current font
     *
     * @param i Font size.
     *
     */
    public void setFontSize(int i) {
        fontsize = i;
        rebuildFont();
    }

    /*
     *
     ** Rebuild the font using the current fontname, fontstyle, and fontsize.
     *
     */
    private void rebuildFont() {

        parse = true;



        if (fontsize <= 0 || fontname == null) {

            font = null;

        } else {



            font = new Font(fontname, fontstyle, fontsize);

        }

    }

    /**
     *
     * @param f Font
     *
     * @return The script font version of the parsed font using the
     *
     * script_fraction variable.
     *
     */
    public Font getScriptFont(Font f) {

        int size;



        if (f == null) {
            return f;
        }



        size = f.getSize();



        if (size <= MINIMUM_SIZE) {
            return f;
        }



        size = (int) ((double) (f.getSize()) * script_fraction + 0.5);



        if (size <= MINIMUM_SIZE) {
            return f;
        }



        return new Font(f.getName(), f.getStyle(), size);

    }

    /**
     *
     * Parse a double value. Precision is 6 figures, with 7 significant
     *
     * figures.
     *
     * @param d double to parse
     *
     * return <i>true</i> if the parse was successful
     *
     */
    public boolean parseDouble(double d) {

        return parseDouble(d, 7, 6, ALGEBRAIC);

    }

    /**
     *
     * Parse a double value. Number of significant figures is 1 greater than
     *
     * the precision.
     *
     * @param d double to parse
     *
     * @param p precision of the number
     *
     * return <i>true</i> if the parse was successful
     *
     */
    public boolean parseDouble(double d, int p) {

        return parseDouble(d, p + 1, p, ALGEBRAIC);

    }

    /**
     *
     * Parse a double value
     *
     * @param d double to parse
     *
     * @param n number of significant figures
     *
     * @param p precision of the number
     *
     * @param f format of the number scientific, algebraic etc.
     *
     * return <i>true</i> if the parse was successful
     *
     */
    public boolean parseDouble(double d, int n, int p, int f) {

        double x = d;

        int left = n - p;

        double right = 0;

        int power;

        int exponent;

        int i;

        StringBuffer s = new StringBuffer(n + 4);





        if (left < 0) {

            return false;

        }



        if (d < 0.0) {

            x = -d;

            s.append("-");

        }





        //System.out.println("parseDouble: value = "+x);



        if (d == 0.0) {
            exponent = 0;
        } else {
            exponent = (int) (Math.floor(log10(x)));
        }





        //System.out.println("parseDouble: exponent = "+exponent);



        power = exponent - (left - 1);



        //System.out.println("parseDouble: power = "+power);



        if (power < 0) {

            for (i = power; i < 0; i++) {
                x *= 10.0;
            }

        } else {

            for (i = 0; i < power; i++) {
                x /= 10.0;
            }

        }



        //System.out.println("parseDouble: adjusted value = "+x);



        left = (int) x;

        s.append(left);



        //System.out.println("parseDouble: left = "+left);



        if (p > 0) {

            s.append('.');

            right = x - left;

            for (i = 0; i < p; i++) {

                right *= 10;

                if (i == p - 1) {
                    right += 0.5;
                }

                s.append((int) (right));

                right -= (int) right;

            }

        }



        //System.out.println("parseDouble: right = "+right);





        if (power != 0) {

            if (f == SCIENTIFIC) {

                s.append('E');

                if (power < 0) {
                    s.append('-');
                } else {
                    s.append('+');
                }

                power = Math.abs(power);

                if (power > 9) {

                    s.append(power);

                } else {

                    s.append('0');

                    s.append(power);

                }

            } else {

                s.append("x10{^");

                s.append(power);

                s.append("}");

            }

        }



        setText(s.toString());



        return true;



    }

    public double log10(double x) throws ArithmeticException {

        if (x <= 0.0) {
            throw new ArithmeticException("range exception");
        }

        return Math.log(x) / 2.30258509299404568401;

    }
}
